package com.smart.village.big.screen.utils;

import com.smart.village.big.screen.domain.Point;
import com.smart.village.big.screen.domain.Polygon;

import java.util.List;

public class PointInPolygonChecker {
    /**
     * 判断点是否在多边形内（包括边界和顶点）
     * @param point 待判断的点
     * @param polygon 多边形区域
     * @return true-在内部或边界上，false-在外部
     */
    public static boolean isPointInPolygon(Point point, Polygon polygon) {
        List<Point> vertices = polygon.getVertices();
        int n = vertices.size();
        boolean inside = false;

        // 1. 检查点是否是多边形的顶点
        for (Point vertex : vertices) {
            if (isSamePoint(point, vertex)) {
                return true;
            }
        }

        // 2. 检查点是否在多边形的边上
        for (int i = 0; i < n; i++) {
            Point p1 = vertices.get(i);
            Point p2 = vertices.get((i + 1) % n);

            if (isPointOnSegment(point, p1, p2)) {
                return true;
            }
        }

        // 3. X轴射线法判断点是否在多边形内部
        for (int i = 0, j = n - 1; i < n; j = i++) {
            Point p1 = vertices.get(i);
            Point p2 = vertices.get(j);

            // 检查射线是否与边相交
            if (((p1.getY() > point.getY()) != (p2.getY() > point.getY())) &&
                    (point.getX() < p1.getX() + (p2.getX() - p1.getX()) *
                            (point.getY() - p1.getY()) / (p2.getY() - p1.getY()))) {
                inside = !inside;
            }
        }

        return inside;
    }

    /**
     * 判断两点是否相同（考虑浮点数精度）
     */
    private static boolean isSamePoint(Point p1, Point p2) {
        return Math.abs(p1.getX() - p2.getX()) < Point.EPSILON &&
                Math.abs(p1.getY() - p2.getY()) < Point.EPSILON;
    }

    /**
     * 判断点是否在线段上
     */
    private static boolean isPointOnSegment(Point p, Point a, Point b) {
        // 1. 检查点是否在a和b的 bounding box 内
        if (p.getX() < Math.min(a.getX(), b.getX()) - Point.EPSILON ||
                p.getX() > Math.max(a.getX(), b.getX()) + Point.EPSILON ||
                p.getY() < Math.min(a.getY(), b.getY()) - Point.EPSILON ||
                p.getY() > Math.max(a.getY(), b.getY()) + Point.EPSILON) {
            return false;
        }

        // 2. 检查点是否在直线ab上（叉积为0）
        double crossProduct = (p.getX() - a.getX()) * (b.getY() - a.getY()) -
                (p.getY() - a.getY()) * (b.getX() - a.getX());
        if (Math.abs(crossProduct) > Point.EPSILON) {
            return false;
        }

        return true;
    }
}
